#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@file: app.py
@time: 2024/12/7  21:00
# @describe:
"""
import pandas as pd
import json
import plotly
import plotly.express as px
from flask import request
from flask import Flask, render_template, redirect, url_for, flash, session
from datetime import datetime
import os
from functools import wraps

app = Flask(__name__)
app.debug = True
app.secret_key = 'your_secret_key_here'  # 用于session加密

# 添加数据缓存
_data_cache = None
_users_cache = None

def login_required(f):
    """登录验证装饰器"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'username' not in session:
            flash('请先登录！', 'warning')
            return redirect(url_for('login'))
        return f(*args, **kwargs)
    return decorated_function

def time_to_seconds(time_str):
    """将时间字符串转换为秒数"""
    if pd.isna(time_str):
        return None
    try:
        h, m, s = map(int, time_str.split(':'))
        return h * 3600 + m * 60 + s
    except:
        return None

def seconds_to_time_str(seconds):
    """将秒数转换为时间字符串"""
    if seconds is None:
        return "-"
    hours = int(seconds // 3600)
    minutes = int((seconds % 3600) // 60)
    seconds = int(seconds % 60)
    return f"{hours:02d}:{minutes:02d}:{seconds:02d}"

def load_data():
    """加载马拉松数据（带缓存）"""
    global _data_cache
    if _data_cache is None:
        _data_cache = pd.read_csv('data/data.csv')
    return _data_cache

def load_users():
    """加载用户数据（带缓存）"""
    global _users_cache
    users_file = 'data/users.csv'
    if not os.path.exists(users_file):
        os.makedirs('data', exist_ok=True)
        df = pd.DataFrame(columns=['username', 'password', 'email', 'register_time'])
        df.to_csv(users_file, index=False)
        _users_cache = df
        return df
    if _users_cache is None:
        _users_cache = pd.read_csv(users_file)
    return _users_cache

def save_user(username, password, email):
    """保存新用户并更新缓存"""
    global _users_cache
    df = load_users()
    new_user = pd.DataFrame({
        'username': [username],
        'password': [password],
        'email': [email],
        'register_time': [datetime.now().strftime('%Y-%m-%d %H:%M:%S')]
    })
    df = pd.concat([df, new_user], ignore_index=True)
    df.to_csv('data/users.csv', index=False)
    _users_cache = df

# 优化图表生成
def optimize_plot(fig):
    """优化Plotly图表配置"""
    fig.update_layout(
        paper_bgcolor='rgba(0,0,0,0)',
        plot_bgcolor='rgba(0,0,0,0)',
        margin=dict(l=20, r=20, t=40, b=20),
        hovermode='closest'
    )
    return fig

@app.route('/')
def index():
    """优化首页加载"""
    df = load_data()
    
    # 计算基础统计数据
    stats = {
        'total_runners': len(df),
        'total_countries': df['Country '].nunique(),
        'total_categories': df['Category'].nunique(),
        'fastest_time': df['Net Time'].min()
    }
    
    # 只获取前10个国家数据
    country_stats = df['Country '].value_counts().head(10)
    country_fig = px.bar(
        x=country_stats.index,
        y=country_stats.values,
        title='参赛国家或地区Top10',
        labels={'x': '国家或地区', 'y': '参赛人数'}
    )
    optimize_plot(country_fig)
    
    return render_template('index.html',
                         stats=stats,
                         country_chart=json.dumps(country_fig, cls=plotly.utils.PlotlyJSONEncoder))

@app.route('/runners')
@login_required
def runners():
    """优化选手列表页面"""
    df = load_data()
    
    # 分页参数
    page = int(request.args.get("page", 1))
    per_page = int(request.args.get('per_page', 50))
    
    # 只获取需要的列
    runners_data = df.iloc[(page-1)*per_page:page*per_page]
    total_pages = (len(df) + per_page - 1) // per_page
    
    return render_template('runners.html', 
                         runners=runners_data.to_dict('records'),
                         total_pages=total_pages,
                         current_page=page)

@app.route('/analysis')
@login_required
def analysis():
    """成绩分析页面"""
    df = load_data()
    
    # 1. 各组别完赛人数统计
    category_stats = df['Category'].value_counts()
    category_fig = px.pie(
        values=category_stats.values,
        names=category_stats.index,
        title='参赛组别分布'
    )
    category_chart = json.dumps(category_fig, cls=plotly.utils.PlotlyJSONEncoder)
    
    # 2. 国家参赛人数Top10
    country_stats = df['Country '].value_counts().head(10)
    country_fig = px.bar(
        x=country_stats.index,
        y=country_stats.values,
        title='参赛国家或地区Top10',
        labels={'x': '国家', 'y': '参赛人数'}
    )
    country_chart = json.dumps(country_fig, cls=plotly.utils.PlotlyJSONEncoder)
    
    # 3. 配速分析
    pace_stats = {}
    checkpoints = ['10km Time', 'Half Way Time', '30km Time', 'Net Time']
    for col in checkpoints:
        times = df[col].apply(time_to_seconds)
        pace_stats[col] = seconds_to_time_str(times.mean())
    
    # 4. 完赛时间分布
    finish_times = df['Net Time'].apply(time_to_seconds).dropna()
    time_bins = list(range(0, int(finish_times.max()) + 1800, 1800))
    hist_fig = px.histogram(
        x=finish_times,
        nbins=len(time_bins)-1,
        title='完赛时间分布',
        labels={'x': '完赛时间', 'y': '人数'}
    )
    hist_fig.update_xaxes(ticktext=[seconds_to_time_str(x) for x in time_bins[::4]],
                         tickvals=time_bins[::4])
    time_chart = json.dumps(hist_fig, cls=plotly.utils.PlotlyJSONEncoder)
    
    # 5. 各检查点平均配速变化
    checkpoint_distances = {
        '10km Time': 10,
        'Half Way Time': 21.0975,
        '30km Time': 30,
        'Net Time': 42.195
    }
    
    pace_data = []
    for point, distance in checkpoint_distances.items():
        times = df[point].apply(time_to_seconds)
        avg_pace = times.mean() / (distance * 60)  # 转换为分钟/公里
        pace_data.append({
            'checkpoint': point.replace(' Time', ''),
            'distance': distance,
            'pace': avg_pace
        })
    
    pace_fig = px.line(
        pace_data,
        x='distance',
        y='pace',
        title='平均配速变化',
        labels={'distance': '距离(km)', 'pace': '配速(分钟/公里)'}
    )
    pace_chart = json.dumps(pace_fig, cls=plotly.utils.PlotlyJSONEncoder)
    
    return render_template('analysis.html', 
                         category_chart=category_chart,
                         country_chart=country_chart,
                         time_chart=time_chart,
                         pace_chart=pace_chart,
                         pace_stats=pace_stats)

@app.route('/search')
@login_required
def search():
    """搜索功能"""
    keyword = request.args.get('q', '')
    df = load_data()
    
    # 搜索参赛号或国家
    results = df[
        (df['Race No'].astype(str).str.contains(keyword, na=False)) |
        (df['Country '].str.contains(keyword, na=False)) 
    ]
    
    return render_template('runners.html',  # 使用runners模板显示搜索结果
                         runners=results.to_dict('records'),
                         total_pages=1,  # 搜索结果不分页
                         current_page=1)

@app.route('/gender-analysis')
@login_required
def gender_analysis():
    """性别分析页面"""
    df = load_data()
    
    # 根据性别分组统计
    gender_groups = df['Category'].str[1].value_counts()  # M/F
    
    # 性别完赛时间箱线图
    finish_times = df.copy()
    finish_times['Gender'] = df['Category'].str[1]
    finish_times['Finish Time (minutes)'] = finish_times['Net Time'].apply(time_to_seconds) / 60
    
    gender_box = px.box(
        finish_times,
        x='Gender',
        y='Finish Time (minutes)',
        title='男女完赛时间分布',
        labels={'Gender': '性别', 'Finish Time (minutes)': '完赛时间（分钟）'}
    )
    
    return render_template('gender_analysis.html',
                         gender_stats=gender_groups.to_dict(),
                         gender_box=json.dumps(gender_box, cls=plotly.utils.PlotlyJSONEncoder))

@app.route('/elite-analysis')
@login_required
def elite_analysis():
    """精英选手分析页面"""
    df = load_data()
    
    # 获取前100名选手数据
    elite_runners = df.head(100)
    
    # 精英选手国家分布
    elite_countries = elite_runners['Country '].value_counts()
    country_pie = px.pie(
        values=elite_countries.values,
        names=elite_countries.index,
        title='前100名选手国家或地区分布'
    )
    
    # 精英选手完赛时间散点图
    elite_scatter = px.scatter(
        elite_runners,
        x=range(1, 101),
        y=elite_runners['Net Time'].apply(time_to_seconds) / 60,
        title='前100名选手完赛时间分布',
        labels={'x': '排名', 'y': '完赛时间（分钟）'}
    )
    
    return render_template('elite_analysis.html',
                         country_pie=json.dumps(country_pie, cls=plotly.utils.PlotlyJSONEncoder),
                         elite_scatter=json.dumps(elite_scatter, cls=plotly.utils.PlotlyJSONEncoder))

@app.route('/checkpoint-analysis')
@login_required
def checkpoint_analysis():
    """分段配速分析页面"""
    df = load_data()
    
    # 计算各个检查点之间的分段用时
    df['10k_split'] = df['10km Time'].apply(time_to_seconds)
    df['21k_split'] = df['Half Way Time'].apply(time_to_seconds) - df['10km Time'].apply(time_to_seconds)
    df['30k_split'] = df['30km Time'].apply(time_to_seconds) - df['Half Way Time'].apply(time_to_seconds)
    df['finish_split'] = df['Net Time'].apply(time_to_seconds) - df['30km Time'].apply(time_to_seconds)
    
    # 创建分段配速箱线图
    splits_data = []
    for split in ['10k_split', '21k_split', '30k_split', 'finish_split']:
        split_times = df[split] / 60  # 转换为分钟
        splits_data.extend([{
            'split': split.replace('_split', ''),
            'time': t
        } for t in split_times if not pd.isna(t)])
    
    splits_box = px.box(
        splits_data,
        x='split',
        y='time',
        title='各分段用时分布',
        labels={'split': '赛段', 'time': '用时（分钟）'}
    )
    
    return render_template('checkpoint_analysis.html',
                         splits_box=json.dumps(splits_box, cls=plotly.utils.PlotlyJSONEncoder))

@app.route('/login', methods=['GET', 'POST'])
def login():
    """登录功能"""
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        
        users = load_users()
        # 打印调试信息
        print(f"尝试登录: 用户名={username}, 密码={password}")
        print("当前用户数据:", users.to_dict('records'))
        
        # 精确匹配用户名和密码
        user = users[
            (users['username'].astype(str).str.strip() == username.strip()) & 
            (users['password'].astype(str).str.strip() == password.strip())
        ]
        
        if not user.empty:
            session['username'] = username
            flash('登录成功！', 'success')
            return redirect(url_for('index'))
        else:
            flash('用户名或密码错误！', 'danger')
            
    return render_template('login.html')

@app.route('/register', methods=['GET', 'POST'])
def register():
    """注册功能"""
    if request.method == 'POST':
        username = request.form.get('username').strip()
        password = request.form.get('password').strip()
        confirm_password = request.form.get('confirm_password').strip()
        email = request.form.get('email').strip()
        
        users = load_users()
        
        # 验证用户名是否已存在
        if not users[users['username'].astype(str).str.strip() == username].empty:
            flash('用户名已存在！', 'danger')
            return render_template('register.html')
            
        # 验证密码
        if password != confirm_password:
            flash('两次输入的密码不一致！', 'danger')
            return render_template('register.html')
            
        # 保存新用户
        save_user(username, password, email)
        flash('注册成功！请登录。', 'success')
        return redirect(url_for('login'))
        
    return render_template('register.html')

@app.route('/logout')
def logout():
    """登出功能"""
    session.pop('username', None)
    flash('已成功登出！', 'success')
    return redirect(url_for('login'))

if __name__ == '__main__':
    # 预加载数据
    load_data()
    load_users()
    # 使用threaded=True来提高并发性能
    app.run(debug=True, threaded=True)